《Android 基础(三十七)》 自定义ViewPagerIndicator

简介

学习Android,自定义View不可避免,之前一直忽视这块内容,现在开始学,应该不算太晚。从常见的ViewPagerIndicator开始,当然,万能的Github上包罗万象,好用的indicator也是不胜枚举,旨在学习自定义View的一般操作过程。

思路

做一个简单的ViewPagerIndicator,只支持平均大小的TextView,支持点,矩形和三角形。

  1. 使用LinearLayout作为父类;
  2. 定义indicator的颜色,高度和半径,当然也可以定义其他的属性;
  3. 使用到Path,Paint,Canvas等图形绘制的内容;
  4. 使用Kotlin

实现

圆形indicator

定义自定义属性

styles.xml

1
2
3
4
5
6
<attr name="y_indicator_color" format="color"/>
<attr name="y_indicator_radius" format="dimension"/>
<declare-styleable name="YVPDotIndicator">
<attr name="y_indicator_color"/>
<attr name="y_indicator_radius"/>
</declare-styleable>

代码实现

通过设置ViewPager,然后从Adapter的getPageTitle方法获取TextView的显示内容,然后添加标签,然后绘制圆形指示器,通过ViewPager的滑动回调方法,设置圆形指示器的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
class YVPDotIndicator : LinearLayout {
private var mStartPos: Float = 0.0F//indicator开始位置
private var mWidthOffset: Int = 0//初始offset

private var mPaint: Paint? = null

private var mIndicatorColor = Color.parseColor("#FFFFFF")//indicator颜色
private var mIndicatorRadius = 2//圆形indicator半径

private var mVp: ViewPager? = null
private var pageListener = InterPageChangeListener()

private var mTabCount: Int? = 0
private var mTabWidth: Float? = 0.0F
private val defaultLayoutParams = LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f)

constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
setWillNotDraw(false)

val dm = resources.displayMetrics

val a = context.theme.obtainStyledAttributes(attrs, R.styleable.YVPDotIndicator, defStyle, 0)
mIndicatorRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mIndicatorRadius.toFloat(), dm).toInt()

mIndicatorColor = a.getColor(R.styleable.YVPDotIndicator_y_indicator_color, Color.parseColor("#FFFFFF"))
mIndicatorRadius = a.getDimensionPixelSize(R.styleable.YVPDotIndicator_y_indicator_radius, 2)
a.recycle()

initPaint()
}

/**
* 设置ViewPager
*/
fun setViewPager(vp: ViewPager) {
mVp = vp
if (vp.adapter == null) {
throw IllegalArgumentException()
}
notifyDataSetChanged()
mVp?.addOnPageChangeListener(pageListener)
}

fun notifyDataSetChanged() {
this.removeAllViews()
mTabCount = mVp?.adapter?.count
for (i in 0..mTabCount?.let { it - 1 } as Int) {
addTextTab(i, mVp?.adapter?.getPageTitle(i).toString())
}
}

fun addTextTab(position: Int, title: String) {
var tab = TextView(context)
tab.text = title
tab.gravity = Gravity.CENTER
tab.setSingleLine()

tab.isFocusable = true
tab.setOnClickListener { mVp?.currentItem = position }

this.addView(tab, position, defaultLayoutParams)
}

/**
* 初始化画笔
*/
private fun initPaint() {
mPaint = Paint()
mPaint?.color = mIndicatorColor
mPaint?.isAntiAlias = true
mPaint?.style = Paint.Style.FILL
}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mTabWidth = (w / childCount).toFloat()
mStartPos = mTabWidth?.let { it/2 } as Float
}

override fun dispatchDraw(canvas: Canvas?) {
canvas?.save()
canvas?.translate(0.0F, height.toFloat())
canvas?.drawCircle(mStartPos + mWidthOffset, -mIndicatorRadius.toFloat(), mIndicatorRadius.toFloat(), mPaint)
canvas?.restore()
super.dispatchDraw(canvas)
}

inner class InterPageChangeListener: ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {

}

override fun onPageSelected(position: Int) {

}

override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
val tabWidth = screenWidth / childCount
mWidthOffset = (tabWidth * position + tabWidth * positionOffset).toInt()
invalidate()
}
}

/**
* 获取屏幕宽度

* @return
*/
private val screenWidth: Int
get() {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
return displayMetrics.widthPixels
}
}

其他Indicator

类似的矩形指示器和三角形指示器,均可按照上面的方式实现。

具体效果

这里写图片描述

示例源码

YVPIndicator

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×